package server

import (
	"context"
	"errors"
	"fmt"
	"net"
	"os"
	"reflect"
	"sync"

	"golang.org/x/sync/errgroup"

	"go-caipu/pkg/api"
	"go-caipu/pkg/infra/log"
	"go-caipu/pkg/registry"
	"go-caipu/pkg/setting"
)

// Options contains parameters for the New function.
type Options struct {
	HomePath    string
	PidFile     string
	Version     string
	Commit      string
	BuildBranch string
	Listener    net.Listener
}
type Server struct {
	context          context.Context
	shutdownFn       context.CancelFunc
	childRoutines    *errgroup.Group
	log              log.Logger
	cfg              *setting.Cfg
	shutdownOnce     sync.Once
	shutdownFinished chan struct{}
	isInitialized    bool
	mtx              sync.Mutex

	pidFile     string
	version     string
	commit      string
	buildBranch string

	backgroundServices []registry.BackgroundService
	HTTPServer         *api.HTTPServer
}

func New(opts Options, cfg *setting.Cfg, httpServer *api.HTTPServer, backgroundServiceProvider registry.BackgroundServiceRegistry) (*Server, error) {
	s, err := newServer(opts, cfg, httpServer, backgroundServiceProvider)
	if err != nil {
		return nil, err
	}

	if err := s.init(); err != nil {
		return nil, err
	}
	cfg.BuildVersion = opts.Version // set build version
	return s, nil
}
func newServer(opts Options, cfg *setting.Cfg, httpServer *api.HTTPServer, backgroundServiceProvider registry.BackgroundServiceRegistry) (*Server, error) {
	rootCtx, shutdownFn := context.WithCancel(context.Background())
	childRoutines, childCtx := errgroup.WithContext(rootCtx)
	s := &Server{
		context:       childCtx,
		childRoutines: childRoutines,
		HTTPServer:    httpServer,
		//provisioningService: provisioningService,
		//roleRegistry:        roleRegistry,
		shutdownFn:         shutdownFn,
		shutdownFinished:   make(chan struct{}),
		log:                log.New("server"),
		cfg:                cfg,
		pidFile:            opts.PidFile,
		version:            opts.Version,
		commit:             opts.Commit,
		buildBranch:        opts.BuildBranch,
		backgroundServices: backgroundServiceProvider.GetServices(),
		//moduleService:       moduleService,
	}

	return s, nil
}

func (s *Server) init() error {
	s.mtx.Lock()
	defer s.mtx.Unlock()

	if s.isInitialized {
		return nil
	}
	s.isInitialized = true

	fmt.Printf("init server no handle")
	return nil
}
func (s *Server) Run() error {
	defer close(s.shutdownFinished)
	if err := s.init(); err != nil {
		return err
	}
	services := s.backgroundServices

	//start background services.
	for _, svc := range services {
		//http_server 没有实现
		if registry.IsDisabled(svc) {
			continue
		}
		service := svc
		serviceName := reflect.TypeOf(service).String()
		s.childRoutines.Go(func() error {
			select {
			case <-s.context.Done():
				return s.context.Err()
			default:
			}
			s.log.Debug("Starting background service", "service", serviceName)
			err := service.Run(s.context)
			// Do not return context.Canceled error since errgroup.Group only
			// returns the first error to the caller - thus we can miss a more
			// interesting error.
			if err != nil && !errors.Is(err, context.Canceled) {
				s.log.Error("Stopped background service", "service", serviceName, "reason", err)
				return fmt.Errorf("%s run error: %w", serviceName, err)
			}
			s.log.Debug("Stopped background service", "service", serviceName, "reason", err)
			return nil
		})
	}
	s.notifySystemd("READY=1")

	s.log.Debug("Waiting on services...")

	return s.childRoutines.Wait()
}
func (s *Server) Shutdown(ctx context.Context, reason string) error {
	fmt.Println("server Shutdown no handle")
	return nil
}

// notifySystemd sends state notifications to systemd.
func (s *Server) notifySystemd(state string) {
	notifySocket := os.Getenv("NOTIFY_SOCKET")
	if notifySocket == "" {
		s.log.Debug(
			"NOTIFY_SOCKET environment variable empty or unset, can't send systemd notification")
		return
	}

	socketAddr := &net.UnixAddr{
		Name: notifySocket,
		Net:  "unixgram",
	}
	conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
	if err != nil {
		s.log.Warn("Failed to connect to systemd", "err", err, "socket", notifySocket)
		return
	}
	defer func() {
		if err := conn.Close(); err != nil {
			s.log.Warn("Failed to close connection", "err", err)
		}
	}()

	_, err = conn.Write([]byte(state))
	if err != nil {
		s.log.Warn("Failed to write notification to systemd", "err", err)
	}
}
